#ifndef MATRIXMANAGEMENT_H_INCLUDED
#define MATRIXMANAGEMENT_H_INCLUDED

#include "common.h"
#include <SDL.h>

//! \brief Generic size structure
typedef struct
{
    Uint16 nbrlign;     //!< Vertical size in pixels
    Uint16 nbrcolumn;   //!< Horizontal size in pixels
} Taille;

//! \brief Generic position structure
typedef struct
{
    Uint16 lign;        //!< Vertical position in pixels
    Uint16 column;      //!< Horizontal position in pixels
} Pos;

//! \brief Generic position structure (Long precision)
typedef struct
{
    unsigned long lign;          //!< Vertical position in pixels (long precision)
    unsigned long column;        //!< Horizontal position in pixels (long precision)
} LongPos;

//! \brief Generic area structure
typedef struct
{
    Uint16 x;   //!< Horizontal position in pixels
    Uint16 y;   //!< Vertical position in pixels
    Uint16 w;   //!< Horizontal size in pixels
    Uint16 h;   //!< Vertical size in pixels
} Area;

//! \brief Generic area structure (Algebraic position)
typedef struct
{
    signed short x;         //!< Horizontal position in pixels (signed)
    signed short y;         //!< Vertical position in pixels (signed)
    unsigned short w;       //!< Horizontal size in pixels
    unsigned short h;       //!< Vertical size in pixels
} SArea;

inline Taille getSizefromArea(Area a) {Taille t = {a.h, a.w}; return t;}
inline Taille getSizefromSArea(SArea a) {Taille t = {a.h, a.w}; return t;}

bool posMatch(Pos* table, Uint16 sz, Uint16 li, Uint16 col);

//! \brief Applies a function to a matrix
//!
//! \param src A matrix to apply the function to
//! \param dest A matrix to store the result
//! \param sz The size of the matrices
//! \param fun The function to be applied
//!
template<typename T1, typename T2> void applyMatrix(T1** src, T2** dest, Taille sz, T2(*fun)(T1))
{
    int li, col;
    for(li=0;li<sz.nbrlign;li++)
    {
        for(col=0;col<sz.nbrcolumn;col++)
        {
            dest[li][col] = (*fun)(src[li][col]);
        }
    }
}

//! \brief Creates a matrix of specified size
//!
//! \param size The size of the matrix
//! \param value A filling value
//!
template<typename T> T** createMatrix (Taille size, T value)
{
    T** matrix;
    int i, j;
    matrix = (T**)malloc(size.nbrlign * sizeof(T*));
    for(i=0;i<size.nbrlign;i++)
    {
        matrix[i] = (T*)malloc(size.nbrcolumn * sizeof(T));
        for(j=0;j<size.nbrcolumn;j++)
        {
            matrix[i][j] = value;
        }
    }
    return matrix;
}

//! \brief Copies a matrix, stretching and filling if needed
//!
//! \param base A matrix to be copied
//! \param area The area which will be copied
//! \param baseSize The base matrix size
//! \param value A filling value
//!
//! \return A copy of base
//!
template<typename T> T** copyMatrix (T** base, SArea area, Taille baseSize, T value)
{
    int i, j;
    T** matrix = (T**)malloc(area.h * sizeof(T*));
    for(i=0;i<area.h;i++)
    {
        matrix[i] = (T*)malloc(area.w * sizeof(T));
        for(j=0;j<area.w;j++)
        {
            if((i+area.y)>=baseSize.nbrlign or (i+area.y)<0 or (j+area.x)>=baseSize.nbrcolumn or (j+area.x)<0)
            {
                matrix[i][j] = value;
            }
            else
            {
                matrix[i][j] = base[i+area.y][j+area.x];
            }
        }
    }
    return matrix;
}

//! \brief Copies a matrix without stretching
//!
//! \param base A matrix to be copied
//! \param dest The destination matrix
//! \param size The base and destination matrix size
//!
template<typename T> void copyMatrixSansMem (T** base, T** dest, Taille size)
{
    int i, j;
    for(i=0;i<size.nbrlign;i++)
    {
        for(j=0;j<size.nbrcolumn;j++)
        {
            dest[i][j] = base[i][j];
        }
    }
}

//! \brief Frees the matrix off memory
//!
//! \param matrix A matrix to free
//! \param size The matrix size
//!
template<typename T> void freeMatrix (T** matrix, Taille size)
{
    int i;
    for(i=0;i<size.nbrlign;i++)
    {
        free(matrix[i]);
    }
    free(matrix);
}

#endif // MATRIXMANAGEMENT_H_INCLUDED
